package com.hp.gxw.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.hp.gxw.shiro.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

/**
 * shiro配置类
 */
@Configuration
public class ShiroConfig {
	
	@Value("${server.servlet.session.timeout}")
    private int tomcatTimeout;

    /**
     * LifecycleBeanPostProcessor，这是个DestructionAwareBeanPostProcessor的子类，
     * 负责org.apache.shiro.util.Initializable类型bean的生命周期的，初始化和销毁。
     * 主要是AuthorizingRealm类的子类，以及EhCacheManager类。
     */
	@Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * HashedCredentialsMatcher，这个类是为了对密码进行编码的，
     * 防止密码在数据库里明码保存，当然在登陆认证的时候，
     * 这个类也负责对form里输入的密码进行编码。
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(2);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }

    /**
     * 需要密码realm
     */
    @Bean
    public NormalRealm normalRealm() {
        NormalRealm realm = new NormalRealm();
        realm.setCredentialsMatcher(hashedCredentialsMatcher());
        //缓存设置
        //realm.setAuthenticationCacheName("authenticationCache");
        //realm.setAuthorizationCacheName("authorizationCache");
        return realm;
    }

    /**
     * 免密登录realm
     *
     * @return
     */
    @Bean
    public FreeRealm freeRealm() {
        FreeRealm realm = new FreeRealm();
        //不需要加密，直接用数据库密码进行登录
        return realm;
    }
    
    /**
     * EhCacheManager，缓存管理，用户登陆成功后，把用户信息和权限信息缓存起来，
     * 然后每次用户请求时，放入用户的session中，如果不设置这个bean，每个请求都会查询一次数据库。
     */
//    @Bean
//    public EhCacheManager ehCacheManager(CacheManager cacheManager) {
//    	EhCacheManager em = new EhCacheManager();
//    	em.setCacheManager(cacheManager);
//    	em.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
//        return em;
//    }

    /**
     * SecurityManager，权限管理，这个类组合了登陆，登出，权限，session的处理，是个比较重要的类。
     * //
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(modularRealmAuthenticator());
        List<Realm> realms = new ArrayList<Realm>();
        //密码登录realm
        realms.add(normalRealm());
        //免密登录realm
        realms.add(freeRealm());
        securityManager.setRealms(realms);
        //缓存管理器
//        securityManager.setCacheManager(ehCacheManager);
        //session管理器
        securityManager.setSessionManager(sessionManager());
        //注入记住我管理器  
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }
    /**
     * 系统自带的Realm管理，主要针对多realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        //自己重写的ModularRealmAuthenticator
        UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }
    
    /**
     * shiro session的管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        //DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        AppSessionManager sessionManager = new AppSessionManager();
        sessionManager.setGlobalSessionTimeout(tomcatTimeout*1000);
        //设置sessionDao对session查询，在查询在线用户service中用到了
        sessionManager.setSessionDAO(sessionDAO());
        //配置session的监听
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        listeners.add(new ShiroSessionListener());
        sessionManager.setSessionListeners(listeners);
        return sessionManager;
    }
 
    @Bean
    public SessionDAO sessionDAO(){
        return new MemorySessionDAO();
    }
 
    @Bean
    public SimpleCookie rememberMeCookie(){
 
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //simpleCookie.setName("jeesite.session.id");
        //如果httyOnly设置为true，则客户端不会暴露给客户端脚本代码，使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击；
        simpleCookie.setHttpOnly(true);
        //30天
        simpleCookie.setMaxAge(259200);
        
        return simpleCookie;
    }
    
    /**  
    * cookie管理对象;  
    * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中  
    * @return  
    */  
    @Bean  
	public CookieRememberMeManager rememberMeManager() {
    	CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j3Y+R1aSn5BOlAA=="));
        return cookieRememberMeManager;
	}

    /**
     * ShiroFilterFactoryBean，是个factorybean，为了生成ShiroFilter。
     * 它主要保持了三项数据，securityManager，filters，filterChainDefinitionManager。
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        
        shiroFilterFactoryBean.setLoginUrl("/");
        //无权跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/");
        
        //设置自定义的过滤器
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        filters.put("user", new AppUserFilter());

        Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
        //静态资源放行，开发权限
        filterChainDefinitionManager.put("/static/**", "anon");
        //验证码
        filterChainDefinitionManager.put("/reFreshRandomCode", "anon");
        //开放登陆接口
        filterChainDefinitionManager.put("/login", "anon");
        filterChainDefinitionManager.put("/app/login", "anon");
        filterChainDefinitionManager.put("/loginOut", "anon");
        filterChainDefinitionManager.put("/graphics/**", "anon");
        filterChainDefinitionManager.put("/mobile/**", "anon");
        filterChainDefinitionManager.put("/api/**", "anon");
        //其余接口一律拦截
        //主要这行代码必须放在所有权限设置的最后，不然会导致所有 url 都被拦截
        //filterChainDefinitionManager.put("/**", "authc");
        filterChainDefinitionManager.put("/**", "user");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);


        return shiroFilterFactoryBean;
    }

    /**
     * DefaultAdvisorAutoProxyCreator，Spring的一个bean，由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * AuthorizationAttributeSourceAdvisor，shiro里实现的Advisor类，
     * 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
        aASA.setSecurityManager(securityManager);
        return aASA;
    }

    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}